import { ref } from 'vue';
import { defineStore } from 'pinia';
import { type Permission, type User } from '@/types/interface/auth_interface';
import urls from '@/api/request/urls';
import Request from '@/api/request/request';
import type { ResponseData } from '@/types/interface/base_interface';
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';

//用于存储用户信息，先用组合式操作作为示例
export const useAuthStore = defineStore(
  'auth',
  () => {
    //1.1 用户token
    const token = ref<string | null>(null); // 用于保存与服务端交互的token。token的值必须要通过getter获取，防止刷新会丢失。之所以在这里设定是之后打算更换存储路径，这里封装方便后期修改

    function setToken(data: string) {
      //设置token值
      token.value = `Bearer ${data}`;
      // 保存token
      sessionStorage.setItem('access_token', `${token.value}`);
    }
    function getToken(): string | null {
      //读取token的值
      if (token.value == null) {
        //如果token值不存在，则从sessionStorate读取。因为刷新页面后，保存在pinia中的数据会丢失
        token.value = sessionStorage.getItem('access_token');
      }
      return token.value;
    }
    function clearToken() {
      //读取token的值
      token.value = null;
      sessionStorage.removeItem('access_token');
    }

    //1.2 用户菜单
    const menus = ref<Permission[]>([]); //定义一个用户的菜单数组
    function setMenus(permissions: Permission[]) {
      //获取菜单数组对象
      menus.value = JSON.parse(JSON.stringify(permissions));
      //对菜单进行筛选处理
      filterDisplayMenus(menus.value);
    }
    function getMenus(): Permission[] {
      //返回菜单数组对象
      return menus.value;
    }
    //从权限中获取显示的菜单集合
    function filterDisplayMenus(permissions: Permission[]) {
      //去掉隐藏菜单（注意：隐藏菜单只是不显示，但是后台传递过来的还是要能够在路由中注册）
      // 遍历后端传递过来的权限信息，然后生成动态路由对象
      for (let index = 0; index < permissions.length; index++) {
        const permission: Permission = permissions[index];
        //如果不是菜单类型或不在前台显示，则移除
        if (permission.type !== 1 || permission.display === false) {
          permissions.splice(index, 1);
          index--;
        }
        //如果存在子目录
        if (permission.children && permission.children.length > 0) {
          filterDisplayMenus(permission.children);
        }
      }
    }
    // 清除路由信息
    function clearMenus() {
      menus.value.splice(0);
    }
    // 从服务端加载账户的路由信息
    async function getMenusFromRemote(): Promise<Permission[]> {
      const responseData: ResponseData = await new Request(urls.API_URL_BACKSTAGE_USER_MENU).get();
      if (responseData) {
        if (responseData.success) {
          // 如果已经登录,则赋值路由信息
          setMenus(responseData.data);
          //生成路由信息并赋值
          setRoutes(initRoutes(responseData.data));
        } else {
          //如果没有登录，清空赋值信息
          clearMenus();
        }
      } else {
        clearMenus();
      }
      return menus.value;
    }

    //1.3 当前登录账户信息
    const loginUser = ref<User>(); //定义一个登录用户的对象

    // 从服务端加载当前登录账户的信息
    async function getLoginUserFromRemote(): Promise<User | undefined> {
      const responseData: ResponseData = await new Request(urls.API_URL_BACKSTAGE_LOGINUSER).get();
      if (responseData) {
        if (responseData.success) {
          // 如果已经登录
          loginUser.value = responseData.data;
          //单独刷新图片的名称，让图片实现自动刷新（后台右上角的登录账户头像）
          if (loginUser.value!.headImgUrl != null && loginUser.value!.headImgUrl.length > 0) {
            loginUser.value!.headImgUrl += '?' + Math.random();
          }
        } else {
          //如果没有登录，清空登录账户信息信息
          handleLogoutSuccess();
        }
      }
      return loginUser.value;
    }

    function getLoginUser(): User | undefined {
      return loginUser.value;
    }

    //1.4 用户拥有的路由集合（router加工后的类型）
    const routes = ref<RouteRecordRaw[]>([]);
    //返回路由数组对象
    function getRoutes(): RouteRecordRaw[] {
      return routes.value;
    }
    //赋值路由
    function setRoutes(data: RouteRecordRaw[]) {
      routes.value = data;
    }
    // 拼接生成路由数据，只对菜单对象进行生成,并返回最终结果
    function initRoutes(permissions: Permission[], rootName?: string): RouteRecordRaw[] {
      // parentRouteName表示上级路由的name属性，用于插入子路由
      let parentRouteName = 'backstage'; // 默认的上级路由
      if (typeof rootName !== 'undefined') {
        // 如果传入了上级路由的名字
        parentRouteName = rootName;
      }
      // 定义要添加的动态路由数组
      const routesNew = <RouteRecordRaw[]>[];
      //引入静态资源（自动查找负责路径规则的所有vue文件进行注册），这个是vite官方推荐的。
      //特别注意：如果不加入这个，而是使用下述的传统路径在开发环境也可以导入动态模块，但是在打包后无法使用
      const modules = import.meta.glob('../views/**/**.vue');
      // 遍历后端传递过来的权限信息，然后生成动态路由对象
      permissions.forEach((permission: Permission) => {
        if (permission.type === 1) {
          // 只对菜单对象生成路由
          const routeNew: RouteRecordRaw = {
            redirect: permission.redirect,
            path: permission.route,
            // name: permission.component, //  自定义的容器名称，则用自定义的
            name: permission.component ? permission.component : permission.name, //  如果没有自定义的容器名称，则用自定义的
            meta: {
              title: permission.name, // 路由的名称，用于导航路径
              requiresAuth: permission.requireAuth, // 是否要求登录后才能访问
              parentRouteName
              // component: EmptyLayer,
            }
          };
          // 设置组件的component
          if (permission.path) {
            // 如果组件的路径存在
            //Object.defineProperty()方法来为对象添加新的属性，这个方法接受三个参数：目标对象、要定义或修改的属性名称以及一个描述符对象，描述符对象包含以下属性：
            Object.defineProperty(routeNew, 'component', {
              //加/* @vite-ignore */的原因如下，因为控制台提示
              //for supported dynamic import formats. If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.
              //特别注意(ljy踩坑1天)：在vite下，不要使用@来表示，例如@/views/${permission.path}
              //特别注意：如果不加入下述被注释的代码，而是使用下述的传统路径在开发环境也可以导入动态模块，但是在打包后无法使用
              // value: () => import(/* @vite-ignore */ `../views${permission.path}`), // 设置属性值
              //只有使用modules这种方式才能导入
              value: modules[`../views${permission.path}`], // 设置属性值
              writable: true, // 允许修改属性值
              enumerable: true, // 允许通过for...in循环遍历属性
              configurable: true // 允许删除属性或修改其特性
            });
          } else {
            // 如果组件路径不存在。则有两种情况：1.正常情况是父容器，那么component不能为空；2.组件暂时未开发，只填写了路由的菜单
            if (permission.children) {
              // 如果还有子组件,说明是父组件。因为要放子组件进去，
              //注意这里不能直接使用roeteNew.component=()=import(/* @vite-ignore */ `@/views${permission.path}`);，因为eslint会提示没有这个属性报错
              Object.defineProperty(routeNew, 'component', {
                // value: { render: (c: any) => c('router-view') }, //父路由没有component时用这个代替，否则打开子组件会显示为空。但是vite下无法使用
                value: () => import('@/components/EmptyLayer.vue'),
                writable: true, // 允许修改属性值
                enumerable: true, // 允许通过for...in循环遍历属性
                configurable: true // 允许删除属性或修改其特性
              });
            }
            // else { // 如果没有子组件，说明可能是没有组件地址的菜单（开发中）
            //   routeNew.redirect = '/backstage/404';
            //   // routeNew.component = Found404;
            // }
          }
          if (permission.url) {
            // 如果有连接地址，则让直接执行跳转
            routeNew.beforeEnter = () => {
              window.location.href = permission.url;
            };
          }
          routesNew.push(routeNew);
          if (permission.children && permission.children.length > 0) {
            // 如果还有子组件
            // 递归处理子路由的component
            routesNew.push(...initRoutes(permission.children, permission.component));
          }
        }
      });
      return routesNew;
    }

    //1.5 面包屑
    const routeMatched = ref<RouteRecordNormalized[]>([]); // 当前路由匹配的完整路径，用于面包屑组件
    // 赋值面包屑组件,value值是路由收尾的to.matched，即获取当前路由的每个路由节点的完整信息
    function setRouteMatched(value: RouteRecordNormalized[]) {
      routeMatched.value = value;
    }

    function getRouteMatched(): RouteRecordNormalized[] {
      return routeMatched.value;
    }

    //2.1 功能性
    //用户登录成功
    function handleLoginSuccess(responseData: ResponseData) {
      // 保存token
      setToken(responseData.data.accessToken);
    }

    //注销用户成功后要处理的信息
    function handleLogoutSuccess() {
      // 移除token
      clearToken();
      //清空路由信息
      clearMenus();
      //清空登录账户信息
      loginUser.value = undefined;
    }

    return {
      setToken,
      getToken,
      setMenus,
      getMenus,
      getMenusFromRemote,
      clearMenus,
      getRoutes,
      initRoutes,
      setRouteMatched,
      getRouteMatched,
      getLoginUserFromRemote,
      getLoginUser,
      handleLoginSuccess,
      handleLogoutSuccess
    };
  },
  {
    //将pinia持久化，需要单独安装pinia持久化插件才能生效。目前无效
    persist: true
  }
);
